from registers import registers
from aluControl import aluControl
from control import control
from alu import alu
from conversions import conversions
from readBinary import ReadBinary
from memory import Memory

class cpu:
    def __init__(self):
        #self.instruction_register = registers(0)
        self.aluControl = aluControl()
        self.control = control()
        self.convert = conversions()        
        self.register = registers(32)
        self.alu = alu()
        self.jumpAlu = alu()
        self.memory = Memory()

    def getRegisterState(self):
        return self.register.getRegisterState()
    
    def getInstructionCount(self):
        return self.instructionCount
    
    def getInstruction(self,i):
        return self.memory.readMemory(i)
    
    def getPc(self):
        return self.pc+4
    
    def loadBinary(self,file):
        loadEXE = ReadBinary()
        loadEXE.loadBinary(file)
        filedata = loadEXE.returnText()
        binlen = len(filedata)
        for i in range(binlen):
            self.memory.writeMemory(0x400000 + i*4, filedata[i])
            self.instructionCount = i

    def initializePipeline(self):
        self.instructionCount = 0
        self.pc = 0x00400000 - 4
        self.nextPC = self.pc
        self.ifidInstruction = 0
        self.ifidPCPlus4 = 0
        self.idexPCPlus4 = 0
        self.idexRegT=0
        self.idexRegD=0
        self.idexRegDst=0
        self.idexSigAluOp=0
        self.idexImmediateValue=0
        self.idexAluSrc = 0
        self.idexReadData2 = 0
        self.idexReadData1 = 0
        self.idexRegWrite = 0
        self.idexBranch = 0
        self.exmemAluResult = 0
        self.exmemRegWrite = 0
        self.exmemWriteRegister = 0
        self.exmemZeroResult = 0
        self.exmemBranch = 0
        
        self.PCSrc = 0

        self.memwbAluResult = 0
        self.memwbRegWrite = 0
        self.memwbWriteRegister = 0
        
        self.aluB=0
        self.idexReadData1=0
        self.aluOp=0
            
    def __cpuStageOne(self):       
        self.ifidPCPlus4 = self.pc + 4
        self.PCSrc = self.exmemBranch & self.exmemZeroResult
        if (self.PCSrc==1):
            self.nextPC = self.exmemPCJump
        else:
            self.nextPC = self.pc+4
        self.pc = self.nextPC;
        self.instruction = self.memory.readMemory( self.pc)
        
        self.ifidInstruction = self.instruction
        
        if (self.memwbRegWrite==1):
            self.register.writeRegister(self.memwbWriteRegister, self.memwbAluResult)
        
    def __cpuStageTwo(self):
        self.oppCode = (self.ifidInstruction & 0b11111100000000000000000000000000) >> 26
        self.readReg1 = (self.ifidInstruction & 0b00000011111000000000000000000000) >> 21 #instruction[25:21]
        self.readReg2 = (self.ifidInstruction & 0b00000000000111110000000000000000) >> 16  #instruction[20:16]
        
        self.idexReadData1 = self.register.readRegister(self.readReg1) 
        self.idexReadData2 = self.register.readRegister(self.readReg2)
        
        self.idexRegT = (self.ifidInstruction & 0b00000000000000001111100000000000) >> 11 #instruction[15:11]
        self.idexRegD = (self.ifidInstruction & 0b00000000000111110000000000000000) >> 16  #instruction[20:16]
        
        self.idexImmediateValue = self.convert.signExtend(self.ifidInstruction & 0xFFFF) #instruction[15:0]
        
        self.control.signalIn(self.oppCode)
        self.idexRegDst = self.control.getRegDst()
        self.idexAluSrc = self.control.getAluSrc()
        self.idexRegWrite = self.control.getRegWrite()
        self.idexSigAluOp = self.control.getAluOp()
        self.idexBranch = self.control.getBranch()
        
        self.idexPCPlus4 = self.ifidPCPlus4
        
    def __cpuStageThree(self):
        self.exmemRegWrite  = self.idexRegWrite
        
        if(self.idexRegDst==1):
            self.exmemWriteRegister = self.idexRegT #instruction[15:11]
        else:
            self.exmemWriteRegister = self.idexRegD  #instruction[20:16]
            
        self.function = (self.idexImmediateValue & 0b111111) #instruction[5:0]
        self.aluControl.oppin(self.function, self.idexSigAluOp)
        self.aluOp = self.aluControl.getAluOp()
        if (self.idexAluSrc == 1):
            self.aluB = self.idexImmediateValue
        else:
            self.aluB = self.idexReadData2
            
        self.alu.compute(self.aluOp,self.idexReadData1,self.aluB)
        self.exmemAluResult = self.alu.getResults()
        self.exmemZeroResult = self.alu.getZero()
        self.exmemBranch = self.idexBranch
        
        self.jumpAlu.compute(0b010, self.idexImmediateValue<<2, self.idexPCPlus4)
        self.exmemPCJump = self.jumpAlu.getResults()

    def __cpuStageFour(self):
        self.memwbAluResult = self.exmemAluResult
        self.memwbRegWrite = self.exmemRegWrite
        self.memwbWriteRegister = self.exmemWriteRegister
        
    def fiveStage(self):
        while(1):
            #run the stages backwards, it will not work otherwise
            self.__cpuStageFour()
            self.__cpuStageThree()
            self.__cpuStageTwo()
            self.__cpuStageOne()
            
    def fiveStageStep(self):
        #run the stages backwards, it will not work otherwise
        self.__cpuStageFour()
        self.__cpuStageThree()
        self.__cpuStageTwo()
        self.__cpuStageOne()
        self.register.printRegisters()
        
#cpu = cpu()
#cpu.initializePipeline()
#cpu.loadBinary("./demoelf")
#cpu.fiveStage()